using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;//AnchorStyles

namespace Camalon.Drawing.Shapes
{

	[ToolboxItem(true)]
	public class Castle : Shape
	{

		public Castle():base( new Rectangle( Point.Empty, new Size( 300,200)))
		{
		}

		CircleType _peakStyle = CircleType.Gear1;
		[Browsable( true )]
		[DefaultValue( CircleType.Gear1 )]
		public CircleType PeakStyle
		{
			get
			{
				return _peakStyle ;
			}
			set
			{
				_peakStyle = value;
				ResetPath();
			}
		}
		
		BorderType _borderType = BorderType.Curve;

		[Browsable( true )]
		[DefaultValue( BorderType.Curve )]
		public BorderType BorderType
		{
			get
			{
				return _borderType ;
			}
			set
			{
				_borderType = value;
				ResetPath();
			}
		}


		private int _peakCount = 10;

		[DefaultValue( 10 )]
		public int PeakCount
		{
			get	{ return _peakCount; }

			set
			{
				if( _peakCount == value) return;

				if( value < 3 ) value = 3;

				_peakCount = value;
				ResetPath();
			}

		}

		private int _peakHeight = 20;

		[DefaultValue( 20 )]
		[RefreshProperties( RefreshProperties.All)]
		public int PeakHeight
		{
			get	{ return _peakHeight; }

			set
			{
				if( _peakHeight == value) return;

				if( value < 0 ) value = 0;
			
				_peakHeight = value;

				ResetPath();
			}

		}

		private AnchorStyles _borderSides = AnchorStyles.Top|AnchorStyles.Left|AnchorStyles.Right|AnchorStyles.Bottom;


		[DefaultValue(AnchorStyles.Top|AnchorStyles.Left|AnchorStyles.Right|AnchorStyles.Bottom)]
		public AnchorStyles BorderSides
		{
			get{ return _borderSides;}
			set
			{ 
				if( _borderSides == value) return;
				_borderSides = value;
				ResetPath();
			}
		}

//		private bool _fitToBounds;
//		[DefaultValue( false)]
//		public bool FitToBounds
//		{
//			get{ return _fitToBounds;}
//			set
//			{ 
//				if( _fitToBounds == value) return;
//				_fitToBounds = value;
//				ResetPath();
//			}
//		}


		private float _tension = 1.0f;
		[DefaultValue(1.0f)]
		[TypeConverter( typeof( PercentConverter))]
		public float CurveTension
		{	
			get{ return _tension;}
			set
			{
				//if( value < 0) value = 0;
				if( value == _tension) return;
				_tension = value;
				ResetPath();
			}

		}

		protected PointF[] PickRectanglePoints( RectangleF bounds, int peaks, CircleType circle)
		{
			PointF[] points = null;

			float peakWidth = bounds.Width / peaks;

			
			if( circle == CircleType.Star1)
			{
				points = new PointF[(2*peaks)+1];
				//points = new PointF[(2*peaks)];

				for( int i= 0; i < peaks; i++)
				{
					points[2*i]		= new PointF(bounds.Left + i*peakWidth, bounds.Bottom );
					points[2*i+1]	= new PointF(bounds.Left + i*peakWidth + (peakWidth/2), bounds.Top);
				}
				
				points[points.Length-1] = new PointF( bounds.Right,bounds.Bottom);// Closing point
				

			}
			else	if( circle == CircleType.Star2)
			{
				points = new PointF[(2*peaks)+1];
				//points = new PointF[(2*peaks)];

				for( int i= 0; i < peaks; i++)
				{
					points[2*i]		= new PointF(bounds.Left + i*peakWidth, bounds.Bottom );
					points[2*i+1]	= new PointF(bounds.Left + i*peakWidth, bounds.Top);
				}

				points[points.Length-1] = new PointF( bounds.Right,bounds.Bottom);// Closing point


			}
			else	if( circle == CircleType.Star3)
			{
				points = new PointF[3*peaks];

				for( int i= 0; i < peaks; i++)
				{
					points[3*i]		= new PointF(bounds.Left + i*peakWidth + (peakWidth/4),		bounds.Bottom );
					points[3*i+1]	= new PointF(bounds.Left + i*peakWidth + (peakWidth/2),		bounds.Top);
					points[3*i+2]	= new PointF(bounds.Left + i*peakWidth + (3*peakWidth/4),	bounds.Bottom );
				}

				PointF[] pts = new PointF[(3*peaks)+2];
				pts[0] = new PointF(bounds.Left, bounds.Bottom);// Starting point
				points.CopyTo(pts,1);
				pts[pts.Length-1] = new PointF( bounds.Right,bounds.Bottom);// Closing point
				points = pts;

			}
			else	if( circle == CircleType.Gear1)
			{
				points = new PointF[(3*peaks) + 1];
				//points = new PointF[(3*peaks)];
				for( int i= 0; i < peaks; i++)
				{
					points[3*i]		= new PointF(bounds.Left + i*peakWidth ,					bounds.Bottom );
					points[3*i+1]	= new PointF(bounds.Left + i*peakWidth + (peakWidth/4),		bounds.Top);
					points[3*i+2]	= new PointF(bounds.Left + i*peakWidth + (3*peakWidth/4),	bounds.Top );
				}

				points[points.Length-1] = new PointF( bounds.Right,bounds.Bottom);// Closing point
			}
			else	if( circle == CircleType.Gear2)
			{
				points = new PointF[4*peaks];
				for( int i= 0; i < peaks; i++)
				{
					points[4*i]		= new PointF(bounds.Left + i*peakWidth ,				 bounds.Top);
					points[4*i+1]	= new PointF(bounds.Left + i*peakWidth + (peakWidth/2) , bounds.Top);
					points[4*i+2]	= new PointF(bounds.Left + i*peakWidth + (peakWidth/2) , bounds.Bottom );
					points[4*i+3]	= new PointF(bounds.Left + i*peakWidth + peakWidth,		 bounds.Bottom );
				}



			}
			else	if( circle == CircleType.Gear3)
			{
				points = new PointF[(4*peaks)+1];
				//points = new PointF[(4*peaks)];
				for( int i= 0; i < peaks; i++)
				{
					points[4*i]		= new PointF(bounds.Left + i*peakWidth ,				 bounds.Bottom);
					points[4*i+1]	= new PointF(bounds.Left + i*peakWidth + (peakWidth/4) , bounds.Top);
					points[4*i+2]	= new PointF(bounds.Left + i*peakWidth + (peakWidth/2) , bounds.Top );
					points[4*i+3]	= new PointF(bounds.Left + i*peakWidth + (3*peakWidth/4),bounds.Bottom );
				}

				points[points.Length-1] = new PointF( bounds.Right,bounds.Bottom);// Closing point

			}

			return points;
		}

		protected void AddRectangleBorders( GraphicsPath path, RectangleF bounds, AnchorStyles borders, int peaks, int borderWidth, BorderType borderStyle, CircleType circle )
		{
			if( path == null) throw new NullReferenceException("AddBorder, GraphicsPath object is null");

			if( borders == AnchorStyles.None || borderWidth == 0 )
			{
				path.AddRectangle( bounds );
				return;
			}

			bool top	= ( borders & AnchorStyles.Top ) > 0;
			bool right	= ( borders & AnchorStyles.Right ) > 0;
			bool bottom = ( borders & AnchorStyles.Bottom ) > 0;
			bool left	= ( borders & AnchorStyles.Left ) > 0;

			RectangleF pBounds	= new RectangleF(0,0,0,borderWidth);

			if( top )
			{
				pBounds.Width = bounds.Width;
				if( left && right )			pBounds.Width -= 2*borderWidth;
				else if( left || right )	pBounds.Width -= borderWidth;
				
				PointF[] points = PickRectanglePoints(pBounds, peaks, circle);;
				
				Matrix m = new Matrix();
				float tx = bounds.X;
				float ty = bounds.Y;
				if( left ) tx += borderWidth;
				m.Translate( tx, ty , MatrixOrder.Append);
				m.TransformPoints(points);
				AddRectangleBorder( points, path, borderStyle );
			}
			else 
			{
				PointF pt1 = bounds.Location;
				PointF pt2 = new PointF(bounds.Right,bounds.Top);
				if( left && right )
				{
					pt1.X += borderWidth;
					pt2.X -= borderWidth;
				}
				else if( left )
				{
					pt1.X += borderWidth;
				}
				else if ( right )
				{
					pt2.X -= borderWidth;
				}

				path.AddLine( pt1, pt2);
			}


			if( right )
			{
				pBounds.Width = bounds.Height;
				if( top && bottom )		pBounds.Width -= 2* borderWidth;
				else if( top || bottom)	pBounds.Width -= borderWidth;

				PointF[] points = PickRectanglePoints(pBounds, peaks, circle);;

				Matrix m = new Matrix();
				m.RotateAt(90,pBounds.Location, MatrixOrder.Append);
				float tx = bounds.X + bounds.Width;
				float ty = bounds.Y ;

				if( top ) ty+= borderWidth;

				m.Translate( tx, ty , MatrixOrder.Append);
				m.TransformPoints(points);

				AddRectangleBorder( points, path, borderStyle );

			}
			else 
			{
				PointF pt1 = new PointF(bounds.Right,bounds.Top);
				PointF pt2 = new PointF(bounds.Right,bounds.Bottom);
				if( top && bottom )
				{
					pt1.Y += borderWidth;
					pt2.Y -= borderWidth;
				}
				else if( top )
				{
					pt1.Y += borderWidth;
				}
				else if ( bottom )
				{
					pt2.Y -= borderWidth;
				}

				path.AddLine( pt1, pt2);
			}


			if( bottom )
			{
				pBounds.Width = bounds.Width;
				if( left && right )			pBounds.Width -= 2*borderWidth;
				else if( left || right )	pBounds.Width -= borderWidth;

				PointF[] points = PickRectanglePoints(pBounds, peaks, circle);;

				using (Matrix m = new Matrix())
				{
					m.RotateAt( 180, RectangleUtil.CenterPoint( pBounds ), MatrixOrder.Append );
				
					float tx = bounds.X;
					float ty = bounds.Bottom - borderWidth ;

					if( left ) tx += borderWidth;
				
					m.Translate( tx, ty , MatrixOrder.Append);
					m.TransformPoints(points);

					AddRectangleBorder( points, path, borderStyle );
				}

			}
			else 
			{
				PointF pt1 = new PointF(bounds.Right,bounds.Bottom);
				PointF pt2 = new PointF(bounds.Left,bounds.Bottom);
				if( left && right )
				{
					pt1.X -= borderWidth;
					pt2.X += borderWidth;
				}
				else if ( right )
				{
					pt1.X -= borderWidth;
				}
				else if( left )
				{
					pt2.X += borderWidth;
				}

				path.AddLine( pt1, pt2);
			}

			if( left )
			{
				pBounds.Width = bounds.Height;
				if( top && bottom )		pBounds.Width -= 2*borderWidth;
				else if( top || bottom)	pBounds.Width -= borderWidth;

				PointF[] points = PickRectanglePoints(pBounds, peaks, circle);;

				Matrix m = new Matrix();
				m.RotateAt(-90,pBounds.Location, MatrixOrder.Append);
				float tx = bounds.X ;
				float ty = bounds.Y + pBounds.Width;

				if( top ) ty+= borderWidth;

				m.Translate( tx, ty, MatrixOrder.Append );
				m.TransformPoints(points);

				AddRectangleBorder( points, path, borderStyle );

			}
			else 
			{
				PointF pt1 = new PointF(bounds.Left,bounds.Bottom);
				PointF pt2 = bounds.Location;
				if( top && bottom )
				{
					pt1.Y -= borderWidth;
					pt2.Y += borderWidth;
				}
				else if ( bottom )
				{
					pt1.Y -= borderWidth;
				}
				else if( top )
				{
					pt2.Y += borderWidth;
				}

				path.AddLine( pt1, pt2);
			}
		}

		private void AddRectangleBorder( PointF[] points, GraphicsPath path, BorderType borderStyle)
		{
			if( borderStyle == BorderType.Line) path.AddLines(points);
			else if( borderStyle == BorderType.Curve) path.AddCurve(points,_tension);
			else
			{
				if( points == null || points.Length < 4 ) 
					throw new InvalidOperationException("A bezier Line must contain at least (4 + 3n) points");
			
				int n = points.Length - 4;
				int rem = int.MaxValue; 
				Math.DivRem( n, 3, out rem );


				if( rem == 0) path.AddBeziers( points );
				else//Close bezier with a line to the end
				{
					PointF[] bezPoints = new PointF[ points.Length - rem ];
					Array.Copy(points, bezPoints, bezPoints.Length);
					
					path.AddBeziers( bezPoints );
					//If you just want to close with a simple line
					int last = points.Length - 1;
					//path.AddLine( points[last - rem] , points[last] );
					PointF[] lines;
					if( rem == 1)
						lines = new PointF[]{ 
												points[ last - 1],
												points[ last - 0]
											} ;
					else
						lines = new PointF[]{ 
												points[ last - 2],
												points[ last - 1],
												points[ last - 0]
											} ;
					path.AddLines( lines);
				}

			}
		
		}


		protected override GraphicsPath MakePath(RectangleF bounds)
		{
			GraphicsPath path = new GraphicsPath();

			AddRectangleBorders( path, bounds, _borderSides, _peakCount, _peakHeight, _borderType, _peakStyle);

			path.CloseAllFigures();
			return path;

		}




	}
}
